package com.lay.http.core;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import com.lay.http.Intercetor;
import com.lay.http.LifeRecycle;
import com.lay.http.RequestFragment;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class NetClient {
    private final int DEF_TIME_OUT = 30000;
    private final int MAX_TASK = 4;
    public ThreadPoolExecutor executor;
    public List<AsyncTask> runningTaskList = new LinkedList<>();
    public List<AsyncTask> readyTaskList = new LinkedList<>();
    private List<Intercetor> intercetorList = new ArrayList<>();
    private int connectTimeout = DEF_TIME_OUT;
    private int readTimeout = DEF_TIME_OUT;
    Object lock = new Object();

    private static NetClient client = new NetClient();
    public static NetClient getClient(){
        return client;
    }

    public void init(){
        if(executor == null){
            executor = new ThreadPoolExecutor(1, MAX_TASK, 60,
                    TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        }
    }

    public void setConnectTimeout(int timeout) {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout can not be negative");
        }
        connectTimeout = timeout;
    }

    public int getConnectTimeout() {
        return connectTimeout;
    }

    public int getReadTimeout() {
        return readTimeout;
    }

    public void setReadTimeout(int timeout) {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout can not be negative");
        }
        readTimeout = timeout;
    }

    public void addIntercetor(Intercetor intercetor){
        intercetorList.add(intercetor);
    }

    public void sendAsync(RequestBody requestBody){
        if(requestBody.getConnectTimeout() == 0){
            requestBody.setConnectTimeout(connectTimeout);
        }
        if(requestBody.getReadTimeout() == 0){
            requestBody.setReadTimeout(readTimeout);
        }
        for(Intercetor intercetor : intercetorList) {
            requestBody.addIntercetor(intercetor);
        }
        requestBody.setH(new Handler());
        AsyncTask task = new AsyncTask(requestBody, true);
        if(runningTaskList.size() >= MAX_TASK){
            readyTaskList.add(task);
            bind(requestBody);
        } else {
            runningTaskList.add(task);
            executor.execute(task);
            if(requestBody instanceof DownloadRequest || requestBody instanceof UploadRequest){
                bind(requestBody);
            }
        }
    }

    public void sendSync(RequestBody requestBody){
        if(requestBody.getConnectTimeout() == 0){
            requestBody.setConnectTimeout(connectTimeout);
        }
        if(requestBody.getReadTimeout() == 0){
            requestBody.setReadTimeout(readTimeout);
        }
        for(Intercetor intercetor : intercetorList) {
            requestBody.addIntercetor(intercetor);
        }
        AsyncTask task = new AsyncTask(requestBody, false);
        if(Looper.getMainLooper() == Looper.myLooper()){
            throw new IllegalArgumentException("不能运行在主线程");
        }
        task.run();
    }

    public GetRequest newGetRequest(String url){
        GetRequest requestBody = new GetRequest(url);
        requestBody.setConnectTimeout(connectTimeout);
        requestBody.setReadTimeout(readTimeout);
        return requestBody;
    }

    public PostRequest newPostRequest(String url){
        PostRequest requestBody = new PostRequest(url);
        requestBody.setConnectTimeout(connectTimeout);
        requestBody.setReadTimeout(readTimeout);
        return requestBody;
    }

    public DownloadRequest newDownloadRequest(String url){
        DownloadRequest requestBody = new DownloadRequest(url);
        requestBody.setConnectTimeout(connectTimeout);
        requestBody.setReadTimeout(readTimeout);
        return requestBody;
    }

    public void bind(final RequestBody requestBody){
        Context context = requestBody.getBindContext();
        if(context == null){
            return;
        }
        requestBody.createTime = System.currentTimeMillis();
        LifeRecycle lr = new LifeRecycle() {
            @Override
            public void onDestroy() {
                synchronized (lock) {
                    if(requestBody instanceof DownloadRequest){
                        Log.d(RequestFragment.FOR_HTTP, "fragment distroy and remove -> " + requestBody);
                        ((DownloadRequest)requestBody).setWorking(false);
                        return;
                    }
                    if(requestBody instanceof UploadRequest){
//                        ((UploadRequest)requestBody).setWorking(false);
                        return;
                    }
                    for(AsyncTask task : readyTaskList){
                        if(task.requestBody == requestBody){
                            Log.d(RequestFragment.FOR_HTTP, "fragment distroy and remove -> " + task.requestBody);
                            readyTaskList.remove(task);
                            return;
                        }
                    }
                }
            }
        };
        if(context instanceof FragmentActivity){
            android.support.v4.app.FragmentManager fm = ((FragmentActivity)context).getSupportFragmentManager();
            RequestFragment f = (RequestFragment)fm.findFragmentByTag(RequestFragment.FOR_HTTP);
            if(f == null){
                f = new RequestFragment();
                fm.beginTransaction().add(f, RequestFragment.FOR_HTTP).commit();
            }
            f.registeLifeRecycle(requestBody.createTime, lr);
            Log.d(RequestFragment.FOR_HTTP, "add -> " + requestBody);
        } else if(context instanceof Activity){
            FragmentManager fm = ((Activity)context).getFragmentManager();
        }
    }

    class AsyncTask implements Runnable {
        public RequestBody requestBody;
        long startTime;
        boolean async;
        public AsyncTask(RequestBody requestBody, boolean async){
            this.requestBody = requestBody;
            startTime = System.currentTimeMillis();
            this.async = async;
        }

        @Override
        public void run() {
            final ResponseBody response = requestBody.process();
            if (requestBody.responseListener != null) {
                if(requestBody.getH() != null){
                    requestBody.getH().post(new Runnable() {
                        @Override
                        public void run() {
                            requestBody.responseListener.onResponse(response);
                        }
                    });
                } else {
                    requestBody.responseListener.onResponse(response);
                }
            }

            // 如果绑定过context，需要在执行结束时，在fragment注销掉该回调
            Context context = requestBody.getBindContext();
            if(context != null) {
                if (context instanceof FragmentActivity) {
                    android.support.v4.app.FragmentManager fm = ((FragmentActivity)context).getSupportFragmentManager();
                    RequestFragment f = (RequestFragment)fm.findFragmentByTag(RequestFragment.FOR_HTTP);
                    if(f != null){
                        Log.d(RequestFragment.FOR_HTTP, "compile -> " + requestBody);
                        f.unregisteLifeRecycle(requestBody.createTime);
                    }
                }
            }
            if(async) {
                synchronized (lock) {
                    runningTaskList.remove(this);
                    for (; ; ) {
                        if (runningTaskList.size() >= MAX_TASK || readyTaskList.size() == 0) {
                            break;
                        }
                        final AsyncTask task = readyTaskList.remove(0);
                        Log.d(RequestFragment.FOR_HTTP, "running task has compile and add -> " + task.requestBody);
                        runningTaskList.add(task);
                        if(requestBody.getH() != null){
                            requestBody.getH().post(new Runnable() {
                                @Override
                                public void run() {
                                    executor.execute(task);
                                }
                            });
                        }
                    }
                }
            }
        }
    }
}